var GownFullModule = ProgressiveObject.extend({
	constructor: function() {},
	module_name: function() {},
	init_module: function(ime,modman) {},
	cleanup_module: function() {}
});

var GownFullModuleManager = Base.extend({
	ime: null,
	mod_obj: null,
	mod_status: null,
	menu_node: null,
	constructor: function(ime)
	{
		this.ime = ime;

		this.mod_obj = {};
		this.mod_status = {};
		this.mod_flag = {};
		this.menu_index = [];
		this.menu_node = new GFMenuNode($GFS('Module'));
	},
	GetModuleType: function(name)
	{
		if(this.mod_status[name]) return this.mod_status[name];
		else return GownFullModuleManager.S_VOID;
	},

	RegisterDownload: function(name,url,hidden)
	{
		if(this.mod_status[name] == GownFullModuleManager.S_LOADED) {
			Debug.trace(0,"GownFullModuleManager: Module %s has already loaded, use another name instead.",name);
			return GownFullModuleManager.R_ERROR;
		}
		if(url) {
			this.mod_status[name] = GownFullModuleManager.S_REMOTE_URL;
			this.mod_obj[name] = url;
			this.mod_flag[name] = hidden ? GownFullModuleManager.F_HIDDEN : 0;
		}
		else {
			this.mod_status[name] = GownFullModuleManager.S_REMOTE;
			this.mod_flag[name] = hidden ? GownFullModuleManager.F_HIDDEN : 0;
		}
		// FIXME: Somewhat wasting CPU time here.
		this.UpdateMenuNode();
		return true;
	},
	RegisterModule: function(obj)
	{
		var b,name;

		name = obj.module_name();

		if(this.mod_status[name] == GownFullModuleManager.S_LOADED) {
			Debug.trace(0,"GownFullModuleManager: Module %s has already loaded, use another name instead.",name);
			return GownFullModuleManager.R_ERROR;
		}
		b = obj.init_module(this.ime,this);
		if(b) {
			this.mod_obj[name] = obj;
			this.mod_status[name] = GownFullModuleManager.S_LOADED;
			if(!this.mod_flag[name]) this.mod_flag[name] = 0;
			this.UpdateMenuNode();
			return GownFullModuleManager.R_LOADED;
		}
		else {
			Debug.trace(0,"GownFullModuleManager: %s::init_module return error, module not registered",name);
			return GownFullModuleManager.R_ERROR;
		}
	},
	LoadModule: function(name,callback)
	{
		var status = this.mod_status[name];
		if(status == GownFullModuleManager.S_REMOTE) {
			var url;
			var data = {};

			url = GownFullConfig.getmod_url + "?objname=" + name;
			data._t = this;
			data.objname = name;
			data.callback = callback;
			DownloadManager.NewDownloadName(name,url,
				function(u,s) {
					if(s) {
						if(u._t.RegisterModule(s)==GownFullModuleManager.R_LOADED && data.callback instanceof Function) data.callback(s);
					}
					else {
						Debug.trace(0,"GownFullModuleManager: Module %s does not exist on server side.",u.objname);
						if(data.callback instanceof Function) data.callback(null);
					}
				},
			data);
			return GownFullModuleManager.R_LOADING;
		}
		else if(status == GownFullModuleManager.S_REMOTE_URL) {
			var data = {};
			var url;

			url = this.mod_obj[name];
			data._t = this;
			data.url = url;
			data.objname = name;
			data.callback = callback;
			DownloadManager.NewDownloadName(name,url,
				function(u,s) {
					if(s) {
						if(u._t.RegisterModule(s)==GownFullModuleManager.R_LOADED && data.callback instanceof Function) data.callback(s);
						
					}
					else {
						Debug.trace(0,"GownFullModuleManager: Module %s with URL=%s does not exist on remote side.",u.objname,u.url);
						if(data.callback instanceof Function) data.callback(null);
					}
				},
			data);
			return GownFullModuleManager.R_LOADING;
		}
		else if(status == GownFullModuleManager.S_DISABLED) {
			var obj = this.mod_obj[name];

			if(obj.init_module(this.ime,this)) {
				this.mod_status[name] = GownFullModuleManager.S_LOADED;
				Debug.trace(0,"GownFullModuleManager: %s enabled", name);
				this.UpdateMenuNode();
				return GownFullModuleManager.R_LOADED;
			}
			else {
				Debug.trace(0,"GownFullModuleManager: %s::init_module return error, module not enabled",name);
				return GownFullModuleManager.R_ERROR;
			}
		}
		else {
			Debug.trace(0,"GownFullModuleManager: Module %s has no REMOTE status.",name);
			return GownFullModuleManager.R_ERROR;
		}
	},
	DisableModule: function(name)
	{
		var s = this.mod_status[name];
	
		if(!s) return false;

		var obj = this.mod_obj[name];

		if(s == GownFullModuleManager.S_LOADED) {
			obj.cleanup_module();
			this.mod_status[name] = GownFullModuleManager.S_DISABLED;
			this.UpdateMenuNode();
			return true;
		}
		else return false;
	},
	Require: function(name,callback)
	{
		var status = this.mod_status[name];
		if(status == GownFullModuleManager.S_LOADED) {
			if(callback) callback(this.mod_obj[name]);
			return GownFullModuleManager.R_LOADED;
		}
		else if(status == GownFullModuleManager.S_REMOTE ||
			status == GownFullModuleManager.S_REMOTE_URL ||
			status == GownFullModuleManager.S_DISABLED) {
			return this.LoadModule(name,callback);
		}
		else {
			if(callback) callback();
			return GownFullModuleManager.R_ERROR;
		}
	},
	GetModule: function(name)
	{
		if(this.mod_status[name] == GownFullModuleManager.S_LOADED) return this.mod_obj[name];
		else return GownFullModuleManager.R_ERROR;
	},
	UpdateMenuNode: function()
	{
		var key,s;
		var i;
		var text;
		var x = this;
		var funhandler = function(pos) { return x.onMenuSelect(pos); };

		i = 0;
		for(key in this.mod_status) {
			//U+2003 1em space
			//U+2714 tick
			//U+2718 cross
			s = this.mod_status[key];
			f = this.mod_flag[key];
			if(f & GownFullModuleManager.F_HIDDEN) continue; // Hidden module from user.
			this.menu_index[i] = key;

			switch(s) {
			case GownFullModuleManager.S_DISABLED:
				text = "\u2718" + key;
				break;
			case GownFullModuleManager.S_LOADED:
				text = "\u2714" + key;
				break;
			case GownFullModuleManager.S_REMOTE:
			case GownFullModuleManager.S_REMOTE_URL:
				text = "\u2003" + key;
				break;
			case GownFullModuleManager.S_VOID:
			default:
				text = 'Error'; // Should not happen, please report to us if you see Error in the menu...
				break;
			}
			this.menu_node.SetItem(i,text,funhandler);
			i++;
		}
		this.menu_node.SetLength(i);
		this.menu_index.length = i;
	},
	onMenuSelect: function(pos)
	{
		var key = this.menu_index[pos];
		var s,obj;
		
		// should be a bug if this occurs!
		// the behaviour of the menu will be "no response" if your are using
		// GenericGownFullMenu.
		if(!key) return false;

		s = this.mod_status[key];
		if(s == GownFullModuleManager.S_LOADED) this.DisableModule(key);
		else this.LoadModule(key);
		return true;
	},
	GetMenuNode: function()
	{
		return this.menu_node;
	}
},
{
	S_VOID: 0,
	S_DISABLED: 1,
	S_LOADED: 2,
	S_REMOTE: 3,
	S_REMOTE_URL: 4,

	F_HIDDEN: 1,

	R_ERROR: 0,
	R_LOADING: 1,
	R_LOADED: 2
});
